package singleflight

import "sync"

// call 代表正在进行中、或已经结束的请求。使用sync.WaitGroup避免重入
type call struct {
	wg sync.WaitGroup
	val interface{}
	err error
}

// 管理不同key的请求(call)

type Group struct {
	mu sync.Mutex
	m map[string]*call
}

// Do方法。接收两个参数，一个参数是key，另一个是函数fn.
// 作用就是针对相同的key， 无论Do被调用多少次，函数fn都只会被调用一次。
func (g *Group) Do(key string, fn func()(interface{}, error)) (interface{}, error) {
	g.mu.Lock()
	if g.m == nil{
		g.m = make(map[string]*call)
	}

	if c, ok := g.m[key]; ok{
		g.mu.Unlock()
		// 如果请求正在进行中，则等待
		c.wg.Wait()
		return c.val, nil
	}

	c := new(call)
	// 发起请求前加锁
	c.wg.Add(1)
	// 添加到map中，表面key已经有对应的请求在处理
	g.m[key] = c
	g.mu.Unlock()

	// 调用fn， 发起请求
	c.val, c.err = fn()
	// 请求结束
	c.wg.Done()

	g.mu.Lock()
	// 更新map
	delete(g.m, key)
	g.mu.Unlock()

	return c.val, c.err
	
}
